package com.example.demo.use_demo.a_mybatisplus_demo.interceptor;

import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.example.demo.utils.TokenUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.SQLException;

/**
 * 有问题后续完善>>>
 *
 * @author farben
 */
@Slf4j
@Component
public class MyPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {

    @SneakyThrows
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        TenantDataScope tenantDataScope = this.getDataScope(ms);
        if (tenantDataScope != null) {
            String sql = boundSql.getSql();
            String tenantDataSql = this.getTenantDataScopeSql(tenantDataScope);
            String finalSql = sql + tenantDataSql;
            Field sqlField = boundSql.getClass().getDeclaredField("sql");
            sqlField.setAccessible(true);
            sqlField.set(boundSql, finalSql);
        }
        InnerInterceptor.super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
    }

    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
    }

    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
    }

    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
    }

    /**
     * 拼接数据权限sql
     */
    private String getTenantDataScopeSql(TenantDataScope tenantDataScope) {
        return " and " + tenantDataScope.tableName() + "." + tenantDataScope.colName() + "=" + TokenUtil.getTenantId();
    }

    /**
     * 获取数据权限注解
     */
    private TenantDataScope getDataScope(MappedStatement mappedStatement) {
        String id = mappedStatement.getId();
        String className = id.substring(0, id.lastIndexOf("."));
        String methodName = id.substring(id.lastIndexOf(".") + 1).split("_")[0];
        Class<?> clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error("ClassNotFoundException", e);
            throw new RuntimeException(e);
        }
        Method[] methods = clazz.getDeclaredMethods();
        if (methods.length > 0) {
            for (Method method : methods) {
                if (methodName.equals(method.getName())) {
                    return method.getAnnotation(TenantDataScope.class);
                }
            }
        }
        return null;
    }
}
